/**
 * Created by Administrator on 17-9-4.
 */
let config = require('./config/config');
let apollo = require('./config/apollo');

// 日志讀寫初始化
require('./util/fileUtil');
let express = require('express');
let tokenService = require('./service/TokenService');
let sysService = require('./service/sysUserService');
let agentService = require('./service/AgentService');
let md5 = require('md5');

let bodyParser = require('body-parser');
let xmlparser = require('express-xml-bodyparser');

let bcryptUtil = require('./util/bcryptUtil');
let objUtil = require('./util/objUtil');
let ipUtil = require('./util/ipUtil');

const {BusiError, SysError} = require('./exception/exception');

process.on('unhandledRejection', function(reason, p){
    //call handler here
    console.log("unhandledRejection, reason:", reason, "p:", p);
 });

let psw = bcryptUtil.hash(md5("12345678"));
console.log("pws is:", psw);

let xml2jsDefaults = {
    explicitArray: false,
    normalize: false,
    normalizeTags: false,
    trim: true
};

let acl = require('acl');

acl = new acl(new acl.memoryBackend());
acl.addUserRoles('admin', 'admin');

// 超级管理员所有权限
acl.allow('admin', [
    '/user/online/cnt',
    '/users/query',
    '/users/incCoin',
    '/users/decCoin',
    '/users/coin/list',
    '/finance/recharge/query',
    '/finance/withdraw/query',
    '/finance/withdraw/accept',
    '/finance/withdraw/refuse',
    '/notice/query',
    '/notice/publish',
    '/notice/settop',
    '/notice/distop',
    '/game/active',
    '/game/info',
    '/question/query',
    '/question/answer',
    '/addRoot',
    '/addChild',
    '/payorder/query'
], '*');

// 超级管理员所有权限
acl.allow('customer', [
    '/users/query',
    '/users/incCoin',
    '/users/decCoin',
    '/users/coin/list',
    '/finance/recharge/query',
    '/finance/withdraw/query',
    '/finance/withdraw/accept',
    '/finance/withdraw/refuse',
    '/notice/query',
    '/notice/publish',
    '/notice/settop',
    '/notice/distop',
    '/game/active',
    '/game/info',
    '/question/query',
    '/question/answer',
    '/addRoot',
    '/addChild'
], '*');

// mysql pool init
// dao init
// redis init

console.logList = [];

class App {
    constructor() {
        this.express = express();

        this.loadPlugin();

        this.express.all('*', function (req, res, next) {
            res.header("Access-Control-Allow-Origin", "*");
            res.header("Access-Control-Allow-Headers", "Origin,X-Requested-With,Content-Type,Accept,params");
            res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
            res.header("X-Powered-By", ' 3.2.1');
            res.header("Content-Type", "application/json;charset=utf-8");
            if (req.originalUrl.indexOf("game/") >= 0 || req.originalUrl.indexOf("getRobot") > 0) {

            } else {
                console.logw('express request ' + req.originalUrl);
            }


            if (req.method === "OPTIONS") {
                res.sendStatus(200);
                let n = 0;
                console.logw('express options ');
            } else {
                next();
                setTimeout(function () {
                    if (!res.finished) {
                        console.logw('request timeout +++++++++++++++++++++++++++++', req.originalUrl);
                        res.send({ code: 500, msg: 'response timeout' });
                    }
                }, 200000);
            }
        });

        this.auth();
    }

    loadPlugin() {
        // 插件
        this.express.use(bodyParser.urlencoded({ extended: false }));

        // parse application/json
        this.express.use(bodyParser.json());
        this.express.use(xmlparser(xml2jsDefaults));
    }

    auth(auths) {

        this.express.use((req, resp, next) => {
            req.ua = req.get('User-Agent');
            next();
        });

        // web接口token鉴权, 此前缀的接口都必须携带token参数
        this.express.use('/auth', (req, resp, next) => {
            console.logw('Accessing the auth section, req info:', JSON.stringify({
                baseUrl: req.baseUrl+req.path,
                ip: req.ip,
                ips: req.ips,
                params: req.body
            }));

            let token = req.body.token;
            let uidDecode = tokenService.validToken(token).then(uid => {
                req.uid = uid;
                next();
            }).catch(err => {
                resp.end(JSON.stringify({ code: 500, msg: 'invalid token' }));
            });
        });

        /**
         * 游戏服与接口授权校验, 此前缀的接口都必须携带authKey参数
         */
        this.express.use('/game', (req, resp, next) => {
            let params = objUtil.clone(req.body);
            delete params.authKey;
            console.logw('Accessing the game auth section, req info:', JSON.stringify({
                baseUrl: req.baseUrl+req.path,
                ip: req.ip,
                ips: req.ips,
                params: params
            }));

            if (!req.body.authKey || req.body.authKey !== config.conf["game.gameServerAuthKey"]) {
                console.logw("authKey nodeWeb "+config.conf["game.gameServerAuthKey"]+" appAuthKey="+req.body.authKey)
                resp.end(JSON.stringify({ code: 500, msg: 'no permission' }));
            } else {
                delete req.body.authKey;
                next();
            }
        });

         /**
         * 游戏服网关与接口授权校验, 此前缀的接口都必须携带authKey参数
         */
        this.express.use('/gateway/auth', (req, resp, next) => {
            let params = objUtil.clone(req.body);
            delete params.authKey;
            console.logw('Accessing the game auth section, req info:', JSON.stringify({
                baseUrl:req.baseUrl+req.path,
                ip: req.ip,
                ips: req.ips,
                params: params
            }));

            if (!req.body.authKey || req.body.authKey !== config.conf["gateway.AuthKey"]) {
                console.logw("gateWay.authKey "+config.conf["gateway.AuthKey"]+" appAuthKey="+req.body.authKey)
                resp.end(JSON.stringify({ code: 500, msg: 'no permission' }));
            } else {
                next();
            }
        });



        // 管理系统接口token鉴权, 此前缀的接口都必须携带token参数
        this.express.use('/admin/auth', (req, resp, next) => {
            let params = objUtil.clone(req.body);
            delete params.authKey;
            console.logw('Accessing the admin auth section, req info:', JSON.stringify({ 
                baseUrl: req.baseUrl+req.path,
                ip: req.ip, 
                ips: req.ips, 
                params: params 
            }));
            try {
                let token = req.body.token;
                sysService.validToken(req.body.token).then(user => {
                    if (user) {
                        acl.isAllowed(user.role, req.path, 'post', function (err, r) {
                            if (r) {
                                console.logw("User:" + user.username + user.role + " is allowed to access:" + req.path);
                                req.username = user.username;
                                next();
                            } else {
                                console.logw("User:" + user.username + user.role + " is not allowed to access:" + req.path);
                                resp.end(JSON.stringify({ code: 500, msg: 'invalid access' }));
                            }
                        });
                    } else {
                        resp.end(JSON.stringify({ code: 501, msg: 'invalid token' }));
                    }
                }).catch(err => {
                    if(err instanceof BusiError){
                        resp.end(JSON.stringify({ code: 501, msg: 'invalid token' }));
                    }else{
                        resp.end(JSON.stringify({ code: 500, msg: 'invalid token' }));
                    }
                });
            } catch (e) {
                resp.end(JSON.stringify({ code: 500, msg: 'invalid token' }));
            }
        });

        // 管理系统接口token鉴权
        this.express.use('/agent/auth', (req, resp, next) => {
            let params = req.body;
            console.logw('Accessing the agent auth section, req info:', JSON.stringify({ 
                baseUrl: req.baseUrl+req.path,
                ip: req.ip, 
                ips: req.ips, 
                params: params 
            }));
            try {
                let token = req.body.token;
                agentService.validToken(req.body.token).then(res => {
                    req.uid = res;
                    next();
                }).catch(err => {
                    console.logw("/agent/auth err:", err);
                    if (err instanceof BusiError){
                        resp.end(JSON.stringify({ code: 501, msg: err.message }));
                    }else{
                        resp.end(JSON.stringify({ code: 500, msg: '系统繁忙, 请稍后再试!' }));
                    }
                });
            } catch (e) {
                console.logw("/agent/auth err:", e);
                resp.end(JSON.stringify({ code: 500, msg: '系统繁忙, 请稍后再试!' }));
            }
        });
    }

    /**
     * 
     * @param {Router} route
     */
    handle(route) {
        let routes = route.getRoutes();
        for (let i = 0; i < routes.length; i++) {
            let item = routes[i];
            switch (item.method) {
                case "GET":
                    this.express.get(item.route, item.handler);
                    break;
                case "POST":
                    this.express.post(item.route, item.handler);
                    break;
            }
        }
    }

    /**
     * 
     * @param {String} port 
     */
    startup(port) {
        this.express.listen(port);

        this.express._router.stack.forEach(function (r) {
            if (r.route && r.route.path) {
                r.route.stack.forEach(function (type) {
                    let method = type.method.toUpperCase();
                    console.logw(method.toUpperCase(), r.route.path);
                });
            }
        });
    }
}

// redis init
let redis = require('./dao/redis');
// mysql init
let mysqlPool = require('./dao/MySQLPool');

apollo.getConfig().then(conf => {
    // 配置初始化
    config.conf = conf;
    redis.init(conf);
    mysqlPool.init(conf);
    let router = require('./router/Router');
    console.logg(conf)

    // schedule init
    require('./schedule/Schedule');

    // handler init
    require('./handler/AdminHandler');
    require('./handler/CoinHandler');
    require('./handler/DkHandler');
    require('./handler/SysHandler');
    require('./handler/WechatHandler');
    require('./handler/UserHandler');
    require('./handler/PayHandler');
    require('./handler/SafeboxHandler');
    require('./handler/PayAcctHandler');
    require('./handler/ExchangeHandler');
    require('./handler/GameHandler');
    require('./handler/RechargeHandler');
    require('./handler/QAndAHandler');
    require('./handler/AgentHandler');
    require('./handler/NoticeHandler');
    require('./handler/AppHandler');
    require('./handler/HallHandler');
    require('./handler/AuthHandler');
    require('./handler/StoreHandler');
    require('./handler/friendHandler');
    require('./handler/ClubHandler');
    require('./handler/UnionHandler')
    require('./handler/onTimerHandler')
    require('./handler/XianliaoHandler');
   // require('./dao/test');
    // yarn add bcrypt@3.0.0
    // let ss = require('./util/userPasswordResetUtil');
    // ss.f();



    // webapp init
    let webapp = new App();
    webapp.handle(router);
    var  webport=conf.webport;
    if(config.webPort)
    {
        webport=config.webPort;
    }

    webapp.startup(webport);

    if(config.userTimer==1)
    {
        setInterval(function()
        {   router.onTimer();
        },1000*5)
    }


    function noop(e){
        console.logw('process exception', e.message);
    }

    process.on('uncaughtException', noop);

    // app.listen(config.webport);
    console.logw('web start at port ', webport);
}).catch(err => {
    console.log("fetch config from config server error:", err);
});
